﻿using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Red.Controls.Curve.ViewModels;
using Red.Controls.TimeLine.Core;

namespace Red.Controls.Curve
{

    public class CircleControl : UserControl
    {
        [DllImport( "user32.dll" )]
        private static extern bool SetCursorPos( int X, int Y );

        #region Data

        private const double _circleSize = 10;
        private UIElement _adornedElement;
        private Point? _cachedMousePos = null;

        #endregion

        #region Properties

        public double CircleSize
        {
            get { return _circleSize; }
        }

        public ControlPoint SourcePoint { get; set; }

        public static readonly DependencyProperty LockedProperty = DependencyProperty.Register( "Locked", 
            typeof( bool ), typeof( CircleControl ), new FrameworkPropertyMetadata( false ) );

        public bool Locked
        {
            get { return (bool)GetValue( LockedProperty ); }
            set { SetValue( LockedProperty, value ); }
        }

        public Action<ControlPoint> OnMoved = null;
        public Action<CircleControl> OnSelection = null;
        public Action<ControlPoint> OnMovementFinsihed = null;

        public TimeRuler HorizontalRuler { get; set; }
        public TimeRuler VerticalRuler { get; set; }

        #endregion

        #region C-tor

        public CircleControl( ControlPoint pm, UIElement adornedElement, TimeRuler horizontalRuler, TimeRuler verticalRuler )
        {
            Width = Height = _circleSize;
            _adornedElement = adornedElement;
            SourcePoint = pm;
            Locked = false;
            HorizontalRuler = horizontalRuler;
            VerticalRuler = verticalRuler;

            MouseUp += EllipseShape_MouseUp;
            MouseDown += EllipseShape_MouseDown;
            MouseMove += EllipseShape_MouseMove;

            pm.PropertyChanged += SourcePoint_PropertyChanged;
        }

        #endregion

        #region Public methods

        public void Arrange( double controlWidth, double controlHeight )
        {
            if ( !HorizontalRuler.IsValueVisibleOnScreen( SourcePoint.Time ) || !VerticalRuler.IsValueVisibleOnScreen( SourcePoint.Value ) )
            {
                Visibility = Visibility.Hidden;
                return;
            }

            Visibility = Visibility.Visible;

            double time = HorizontalRuler.ValueToScreenOffset( SourcePoint.Time );
            double value = VerticalRuler.ValueToScreenOffset( SourcePoint.Value );
            Arrange( new Rect( time - CircleSize / 2, controlHeight - value - CircleSize / 2, CircleSize, CircleSize ) );
        }

        #endregion

        #region Event handlers

        private void SourcePoint_PropertyChanged( object sender, System.ComponentModel.PropertyChangedEventArgs e )
        {
            if ( e.PropertyName == "Time" || e.PropertyName == "Value" || e.PropertyName == "Coordinates" )
            {
                Arrange( _adornedElement.RenderSize.Width, _adornedElement.RenderSize.Height );
            }
        }

        private void EllipseShape_MouseMove( object sender, MouseEventArgs e )
        {
            if ( IsMouseCaptured && e.LeftButton == MouseButtonState.Pressed && !Locked )
            {
                Point p = e.GetPosition( _adornedElement );

                if ( p.X < 0 || p.Y < 0 || p.X > _adornedElement.RenderSize.Width || p.Y > _adornedElement.RenderSize.Height )
                {
                    p = _adornedElement.PointToScreen( _cachedMousePos.Value );
                    SetCursorPos( (int)p.X, (int)p.Y );
                    return;
                }

                Vector delta = p - _cachedMousePos.Value;
                _cachedMousePos = p;

                double oldTime = SourcePoint.Time;
                double oldValue = SourcePoint.Value;

                SourcePoint.Time = Math.Max( HorizontalRuler.StartValue, Math.Min(
                    HorizontalRuler.CurrentEndValue, SourcePoint.Time + delta.X / HorizontalRuler.PixelToValueRatio ) );
                SourcePoint.Value = Math.Max( VerticalRuler.StartValue, Math.Min(
                    VerticalRuler.CurrentEndValue, SourcePoint.Value - delta.Y / VerticalRuler.PixelToValueRatio ) );

                if ( OnMoved != null )
                {
                    OnMoved( SourcePoint );
                }
            }
        }

        private void EllipseShape_MouseUp( object sender, MouseButtonEventArgs e )
        {
            if ( _cachedMousePos == null )
            {
                return;
            }

            Mouse.OverrideCursor = null;
            ReleaseMouseCapture();

            if ( OnMovementFinsihed != null )
            {
                OnMovementFinsihed( SourcePoint );
            }
        }

        private void EllipseShape_MouseDown( object sender, MouseButtonEventArgs e )
        {
            _cachedMousePos = e.GetPosition( _adornedElement );
            if ( OnSelection != null )
            {
                OnSelection( this );
            }

            if ( e.ClickCount == 2 )
            {
                // don't override cursor if it's double click
                return;
            }

            Mouse.OverrideCursor = Cursors.None;
            CaptureMouse();
        }

        #endregion
    }
}
